﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Threading;

using OCXS.OCXSServiceLibrary;
using OCXS.OCXSServiceLibrary.OPCXMLDA10;
using OCXS.OCXSServiceLibrary.OCXSEnvironment;
using Conf = OCXS.OCXSServiceLibrary.Configuration;
using Admin = OCXS.OCXSServiceLibrary.Administration;
using OCXS.OCXSServiceLibrary.Utils;

namespace OCXS.OCXSClient {

    /// <summary>
    /// This class contains a basic client logic to show how a proper client
    /// should be written using OCXS Library.
    /// </summary>
    /// <remarks>
    /// Please see <see cref="OCXS.OCXSServiceLibrary.OCXSService"/> for more detailed information about
    /// supported and unsupported features.
    /// </remarks>
    class Program {

        private const string EndpointAddress = "http://localhost:82/OCXS/OCXS.svc";

        static void Main(string[] args) {
    
            //GetStatus("en-us", "0x00FF");
            //Browse("en-us", "0x00FF", "Root/Fold1/", "", true);
            //GetProperties("en-us", "0x00FF", true);
            //Read("en-us", "0x00FF");
            //Write("en-us", "0x00FF", false);

            /*SubscribeResponse sr = Subscribe("en-us", "0x00FF", true, 10000);

            SubscriptionPolledRefreshResponse sprr = SubscriptionPolledRefresh(
                "en-us",
                "0x0100",
                new string[1] { sr.ServerSubHandle },
                DateTime.Now + new TimeSpan(0, 0, 10),
                5000,
                false);

            SubscriptionCancel(sr.ServerSubHandle, "0x0101");*/


            System.Threading.Thread.Sleep(80000);
        }

        /// <summary>
        /// Gets the channel to contact OCXS Service.
        /// </summary>
        /// <param name="endpoint">The URL to OCXS Service.</param>
        /// <returns>The channel to contact OCXS Service.</returns>
        public static IOCXSService GetOCXSChannel(string endpoint) {
            // Create the service endpoint
            ServiceEndpoint httpEndpoint =
                new ServiceEndpoint(
                    ContractDescription.GetContract(typeof(IOCXSService)),
                    new BasicHttpBinding(),
                    new EndpointAddress(endpoint));

            // Create channel factory and get proper channel for service.
            ChannelFactory<IOCXSService> channelFactory = new ChannelFactory<IOCXSService>(httpEndpoint);

            IOCXSService svc = channelFactory.CreateChannel();

            return svc;
        }

        #region OPC Calls

        /// <summary>
        /// Performs GetStatus request, printing and returning response.
        /// </summary>
        /// <param name="localeID">Locale id for the request.</param>
        /// <param name="clientRequestHandle">Client request handle (identifier) for the request.</param>
        /// <returns>The response to the GetStatus request.</returns>
        public static GetStatusResponse GetStatus(string localeID, string clientRequestHandle) {
            // Get the channel
            IOCXSService svc = GetOCXSChannel(EndpointAddress);

            // Create the request.
            GetStatusRequest GSR = new GetStatusRequest(localeID, clientRequestHandle);

            // Perform the call to service and get the response.
            GetStatusResponse res = svc.GetStatus(GSR);

            PrintResponse(res);

            return res;
        }


        /// <summary>
        /// Performs Browse request, printing and returning response.
        /// </summary>
        /// <param name="localeID">Locale id for the request.</param>
        /// <param name="clientRequestHandle">Client request handle (identifier) for the request.</param>
        /// <param name="itemPath">ItemPath corresponding to the Item to be browsed.</param>
        /// <param name="itemName">ItemName corresponding to the Item to be browsed.</param>
        /// <param name="returnPropertyValues">Specifies whether or not properties must be returned along with the response.</param>
        /// <param name="propertyNames"><b>Not supported feature.</b> See OPC XML DA 1.0 specification for more description about this field.</param>
        /// <param name="continuationPoint"><b>Not supported feature.</b> See OPC XML DA 1.0 specification for more description about this field.</param>
        /// <param name="maxElementsReturned"><b>Not supported feature.</b> See OPC XML DA 1.0 specification for more description about this field.</param>
        /// <param name="browsingFilter"><b>Not supported feature.</b> See OPC XML DA 1.0 specification for more description about this field.</param>
        /// <param name="elementNameFilter"><b>Not supported feature.</b> See OPC XML DA 1.0 specification for more description about this field.</param>
        /// <param name="vendorFilter"><b>Not supported feature.</b> See OPC XML DA 1.0 specification for more description about this field.</param>
        /// <param name="returnAllProperties"><b>Not supported feature.</b> See OPC XML DA 1.0 specification for more description about this field.</param>
        /// <param name="returnErrorText"><b>Not supported feature.</b> See OPC XML DA 1.0 specification for more description about this field.</param>
        /// <returns>The response to the Browse request.</returns>
        public static BrowseResponse Browse(
                string localeID,
                string clientRequestHandle,
                string itemPath,
                string itemName,
                bool returnPropertyValues,
                // Not supported features down here
                System.Xml.XmlQualifiedName[] propertyNames = null,
                string continuationPoint = "",
                int maxElementsReturned = 0,
                browseFilter browsingFilter = browseFilter.all,
                string elementNameFilter = "",
                string vendorFilter = "",
                bool returnAllProperties = true,
                bool returnErrorText = true)
        {
            // Get the channel.
            IOCXSService svc = GetOCXSChannel(EndpointAddress);

            // Create the request.
            BrowseRequest BR = 
                new BrowseRequest(
                    propertyNames,
                    localeID,
                    clientRequestHandle,
                    itemPath,
                    itemName,
                    continuationPoint,
                    maxElementsReturned,
                    browsingFilter,
                    elementNameFilter,
                    vendorFilter,
                    returnAllProperties,
                    returnPropertyValues,
                    returnErrorText);

            // Perform the call to service and get the response.
            BrowseResponse res = svc.Browse(BR);

            PrintResponse(res);

            return res;
        }

        /// <summary>
        /// Performs GetProperties request, printing and returning response.
        /// </summary>
        /// <param name="localeID">Locale id for the request.</param>
        /// <param name="clientRequestHandle">Client request handle (identifier) for the request.</param>
        /// <param name="returnPropertyValues">Specifies whether or not property values must be returned along with the response.</param>
        /// <param name="returnAllProperties">Specifies whether or not property all properties must be returned along with the response.</param>
        /// <param name="itemIDs">Array contaning Item identifiers whose properties should be get.</param>
        /// <param name="itemPath"><b>Not supported feature.</b> See OPC XML DA 1.0 specification for more description about this field.</param>
        /// <param name="propertyNames"><b>Not supported feature.</b> See OPC XML DA 1.0 specification for more description about this field.</param>
        /// <param name="returnErrorText"><b>Not supported feature.</b> See OPC XML DA 1.0 specification for more description about this field.</param>
        /// <returns>The response to the GetProperties request.</returns>
        public static GetPropertiesResponse GetProperties(
                string localeID,
                string clientRequestHandle,
                bool returnPropertyValues,
                bool returnAllProperties = true,
                ItemIdentifier[] itemIDs = null,
                // Not supported features down here
                string itemPath = "",
                System.Xml.XmlQualifiedName[] propertyNames = null,
                bool returnErrorText = false) 
        {
            // Get the channel.
            IOCXSService svc = GetOCXSChannel(EndpointAddress);

            // Let's create a valid ItemIdentifier[] if not provided.
            // This way is shown how to create a valid ItemIdentifier array.
            ItemIdentifier[] ids = itemIDs;
            if (ids == null) {
                
                // There are two 'main ways' to obtain an ItemIdentifier[]:
                //   1. Creating a static fixed size array.
                //
                //   2. Create a List<ItemIdentifier> and call ToArray() method
                //      when finished adding ItemIdentifiers.
                //
                // The solution (2) follows.

                // Imagine having in input some pair containing (ItemPath, ItemName).
                // NOTE: This is just an example about "having something in input", not
                //       meant to be the best solution to specify the Items you want.
                //       Acting this way, ItemPath corresponds to the Key, while ItemName 
                //       corresponds to Value.
                KeyValuePair<string, string>[] input = 
                    new KeyValuePair<string, string>[2] { 
                        new KeyValuePair<string, string>("Root/Fold1/", "MyVar1"), // this is a valid variable
                        new KeyValuePair<string, string>("Root/", "MyVar45")       // this is an invalid variable
                    }; 

                List<ItemIdentifier> l = new List<ItemIdentifier>();

                // For each element in input create an ItemIdentifier.
                foreach (KeyValuePair<string, string> el in input) {
                    ItemIdentifier id = new ItemIdentifier();
                    id.ItemPath = el.Key;
                    id.ItemName = el.Value;

                    l.Add(id);
                }

                // Once added every input element, convert the list to array and
                // the trick is done.
                ids = l.ToArray();
            }

            // Create the request. Mind that formal parameters with default 
            // values usually correspond to not supported features.
            GetPropertiesRequest GPR =
                new GetPropertiesRequest(
                    ids,
                    propertyNames,
                    localeID,
                    clientRequestHandle,
                    itemPath,
                    returnAllProperties,
                    returnPropertyValues,
                    returnErrorText);

            // Perform the call to service and get the response.
            GetPropertiesResponse res = svc.GetProperties(GPR);

            PrintResponse(res);

            return res;
        }

        /// <summary>
        /// Performs Read request, printing and returning response.
        /// </summary>
        /// <param name="localeID">Locale id for the request.</param>
        /// <param name="clientRequestHandle">Client request handle (identifier) for the request.</param>
        /// <param name="returnItemName">Specifies whether or not Item names must be returned along with the response.</param>
        /// <param name="returnItemPath">Specifies whether or not Item paths must be returned along with the response.</param>
        /// <param name="returnItemTime">Specifies whether or not Item timestamp must be returned along with the response.</param>
        /// <param name="returnDiagnosticInfo">Specifies whether or not Item diagnostics information must be returned along with the response.</param>
        /// <param name="returnErrorText">Specifies whether or not detailed error information must be returned along with the response, if any.</param>
        /// <param name="requestItemList">Array containing information about Items to be read.</param>
        /// <param name="requestDeadlineSpecified"><b>Not supported feature.</b> See OPC XML DA 1.0 specification for more description about this field.</param>
        /// <returns>The response to the Read request.</returns>
        public static ReadResponse Read(
            string localeID,
            string clientRequestHandle,
            bool returnItemName = true,
            bool returnItemPath = true,
            bool returnItemTime = true,
            bool returnDiagnosticInfo = false,
            bool returnErrorText = false,
            ReadRequestItemList requestItemList = null,
            // Not supported features down here
            bool requestDeadlineSpecified = false)
        {
            // Get the channel.
            IOCXSService svc = GetOCXSChannel(EndpointAddress);

            // Create request options, for ReadRequest.
            RequestOptions ro = new RequestOptions();
            ro.ClientRequestHandle = clientRequestHandle;
            ro.LocaleID = localeID;
            ro.RequestDeadline = DateTime.Parse("2001-09-11");
            ro.RequestDeadlineSpecified = requestDeadlineSpecified;
            ro.ReturnDiagnosticInfo = returnDiagnosticInfo;
            ro.ReturnErrorText = returnErrorText;
            ro.ReturnItemName = returnItemName;
            ro.ReturnItemPath = returnItemPath;
            ro.ReturnItemTime = returnItemTime;

            // Let's create a valid ReadRequestItemList if not provided.
            // This way is shown how to create a valid ReadRequestItemList.
            ReadRequestItemList requestItems = requestItemList;
            if (requestItems == null) {
                requestItems = new ReadRequestItemList();

                // requestItems.Items is a ReadRequestItem[].
                // There are two 'main ways' to obtain an ReadRequestItem[]:
                //   1. Creating a static fixed size array.
                //
                //   2. Create a List<ReadRequestItem> and call ToArray() method
                //      when finished adding ReadRequestItems.
                //
                // The solution (1) follows.
                requestItems.Items = new ReadRequestItem[2];

                requestItems.Items[0] = new ReadRequestItem();
                requestItems.Items[0].ClientItemHandle = clientRequestHandle;
                requestItems.Items[0].ItemName = "MyVar1"; // valid name in default config
                requestItems.Items[0].ItemPath = "Root/Fold1/";
                // The following parameters are not supported.
                requestItems.Items[0].MaxAge = 0;
                requestItems.Items[0].MaxAgeSpecified = false;
                requestItems.Items[0].ReqType = null;

                requestItems.Items[1] = new ReadRequestItem();
                requestItems.Items[1].ClientItemHandle = clientRequestHandle;
                requestItems.Items[1].ItemName = "MyVar3"; // invalid name in default config
                requestItems.Items[1].ItemPath = "Root/Fold1/";
                // The following parameters are not supported.
                requestItems.Items[1].MaxAge = 0;
                requestItems.Items[1].MaxAgeSpecified = false;
                requestItems.Items[1].ReqType = null;

                // The following parameters are not supported.
                requestItems.ItemPath = null;
                requestItems.MaxAge = 0;
                requestItems.MaxAgeSpecified = false;
                requestItems.ReqType = null;
            }

            // Create the request. Mind that formal parameters with default 
            // values usually correspond to not supported features.
            ReadRequest RR = new ReadRequest(ro, requestItems);

            // Perform the call to service and get the response.
            ReadResponse res = svc.Read(RR);

            PrintResponse(res);

            return res;
        }

        /// <summary>
        /// Performs Write request, printing and returning response.
        /// </summary>
        /// <param name="localeID">Locale id for the request.</param>
        /// <param name="clientRequestHandle">Client request handle (identifier) for the request.</param>
        /// <param name="returnValues">Specifies whether or not Item values must be returned along with the response. </param>
        /// <param name="returnItemName">Specifies whether or not Item names must be returned along with the response.</param>
        /// <param name="returnItemPath">Specifies whether or not Item paths must be returned along with the response.</param>
        /// <param name="returnItemTime">Specifies whether or not Item timestamp must be returned along with the response.</param>
        /// <param name="returnDiagnosticInfo">Specifies whether or not Item diagnostics information must be returned along with the response.</param>
        /// <param name="returnErrorText">Specifies whether or not detailed error information must be returned along with the response, if any.</param>
        /// <param name="requestItemList">Array containing information about Items to be written.</param>
        /// <param name="requestDeadlineSpecified"><b>Not supported feature.</b> See OPC XML DA 1.0 specification for more description about this field.</param>
        /// <returns>The response to the Write request.</returns>
        public static WriteResponse Write(
            string localeID,
            string clientRequestHandle,
            bool returnValues,
            bool returnItemName = true,
            bool returnItemPath = true,
            bool returnItemTime = true,
            bool returnDiagnosticInfo = false,
            bool returnErrorText = false,
            WriteRequestItemList requestItemList = null,
            // Not supported features down here
            bool requestDeadlineSpecified = false) 
        {
            // Get the channel.
            IOCXSService svc = GetOCXSChannel(EndpointAddress);

            // Create request options, for ReadRequest.
            RequestOptions ro = new RequestOptions();
            ro.ClientRequestHandle = clientRequestHandle;
            ro.LocaleID = localeID;
            ro.RequestDeadline = DateTime.Parse("2001-09-11");
            ro.RequestDeadlineSpecified = requestDeadlineSpecified;
            ro.ReturnDiagnosticInfo = returnDiagnosticInfo;
            ro.ReturnErrorText = returnErrorText;
            ro.ReturnItemName = returnItemName;
            ro.ReturnItemPath = returnItemPath;
            ro.ReturnItemTime = returnItemTime;

            // Let's create a valid WriteRequestItemList if not provided.
            // This way is shown how to create a valid ReadRequestItemList.
            WriteRequestItemList requestItems = requestItemList;
            if (requestItems == null) {
                requestItems = new WriteRequestItemList();

                // requestItems.Items is an ItemValue[].
                // There are two 'main ways' to obtain an ItemValue[]:
                //   1. Creating a static fixed size array.
                //
                //   2. Create a List<ItemValue> and call ToArray() method
                //      when finished adding ItemValues.
                //
                // The solution (1) follows.
                requestItems.Items = new ItemValue[2];

                requestItems.Items[0] = new ItemValue();
                requestItems.Items[0].DiagnosticInfo = null;
                requestItems.Items[0].ItemPath = "Root/Fold1/";
                requestItems.Items[0].ItemName = "MyVar1";
                requestItems.Items[0].ClientItemHandle = clientRequestHandle;
                requestItems.Items[0].ResultID = null;
                requestItems.Items[0].ValueTypeQualifier = new System.Xml.XmlQualifiedName("float", "xs");
                requestItems.Items[0].Value = "28";
                requestItems.Items[0].Timestamp = DateTime.Now;
                requestItems.Items[0].TimestampSpecified = true;
                requestItems.Items[0].Quality = null;

                requestItems.Items[1] = new ItemValue();
                requestItems.Items[1].DiagnosticInfo = null;
                requestItems.Items[1].ItemPath = "Root/Fold1/";
                requestItems.Items[1].ItemName = "MyVar666";
                requestItems.Items[1].ClientItemHandle = clientRequestHandle;
                requestItems.Items[1].ResultID = null;
                requestItems.Items[1].ValueTypeQualifier = new System.Xml.XmlQualifiedName("float", "xs");
                requestItems.Items[1].Value = "42";
                requestItems.Items[1].Timestamp = DateTime.Now;
                requestItems.Items[1].TimestampSpecified = true;
                requestItems.Items[1].Quality = null;


                requestItems.ItemPath = null;
            }

            // Create the request. Mind that formal parameters with default 
            // values usually correspond to not supported features.
            WriteRequest WR = new WriteRequest(ro, requestItems, returnValues);

            WriteResponse res = svc.Write(WR);

            PrintResponse(res);

            return res;
        }

        /// <summary>
        /// Performs Subscribe request, printing and returning response.
        /// </summary>
        /// <param name="localeID">Locale id for the request.</param>
        /// <param name="clientRequestHandle">Client request handle (identifier) for the request.</param>
        /// <param name="returnValues">Specifies whether or not Item values must be returned along with the response.</param>
        /// <param name="pingRate">The time, in milliseconds, after which the subscription expires (timeout).</param>
        /// <param name="returnItemName">Specifies whether or not Item names must be returned along with the response.</param>
        /// <param name="returnItemPath">Specifies whether or not Item paths must be returned along with the response.</param>
        /// <param name="returnItemTime">Specifies whether or not Item timestamp must be returned along with the response.</param>
        /// <param name="returnDiagnosticInfo">Specifies whether or not Item diagnostics information must be returned along with the response.</param>
        /// <param name="returnErrorText">Specifies whether or not detailed error information must be returned along with the response, if any.</param>
        /// <param name="requestItemList">Array containing information about Items to be written.</param>
        /// <param name="requestDeadlineSpecified"><b>Not supported feature.</b> See OPC XML DA 1.0 specification for more description about this field.</param>
        /// <returns>The response to the Subscribe request.</returns>
        public static SubscribeResponse Subscribe(
            string localeID,
            string clientRequestHandle,
            bool returnValues,
            int pingRate,
            bool returnItemName = true,
            bool returnItemPath = true,
            bool returnItemTime = true,
            bool returnDiagnosticInfo = false,
            bool returnErrorText = false,
            SubscribeRequestItemList requestItemList = null,
            // Not supported features down here
            bool requestDeadlineSpecified = false)
        {
            // Get the channel.
            IOCXSService svc = GetOCXSChannel(EndpointAddress);

            // Create request options, for ReadRequest.
            RequestOptions ro = new RequestOptions();
            ro.ClientRequestHandle = clientRequestHandle;
            ro.LocaleID = localeID;
            ro.RequestDeadline = DateTime.Parse("2001-09-11");
            ro.RequestDeadlineSpecified = requestDeadlineSpecified;
            ro.ReturnDiagnosticInfo = returnDiagnosticInfo;
            ro.ReturnErrorText = returnErrorText;
            ro.ReturnItemName = returnItemName;
            ro.ReturnItemPath = returnItemPath;
            ro.ReturnItemTime = returnItemTime;

            SubscribeRequestItemList requestItems = requestItemList;

            // Let's create a valid SubscribeRequestItemList if not provided.
            // This way is shown how to create a valid ReadRequestItemList.
            if (requestItems == null) {
                requestItems = new SubscribeRequestItemList();

                // RequestedSamplingRate will be valid for all items in
                // this subscription.
                requestItems.RequestedSamplingRate = 1000;
                requestItems.RequestedSamplingRateSpecified = true;


                // requestItems.Items is a SubscribeRequestItem[].
                // There are two 'main ways' to obtain an SubscribeRequestItem[]:
                //   1. Creating a static fixed size array.
                //
                //   2. Create a List<SubscribeRequestItem> and call ToArray() method
                //      when finished adding ItemValues.
                //
                // The solution (1) follows.
                requestItems.Items = new SubscribeRequestItem[2];

                requestItems.Items[0] = new SubscribeRequestItem();
                requestItems.Items[0].ClientItemHandle = clientRequestHandle;
                requestItems.Items[0].Deadband = 0.5f;
                requestItems.Items[0].DeadbandSpecified = true;
                requestItems.Items[0].ItemName = "MyVar4";
                requestItems.Items[0].ItemPath = "Root/Fold2/Fold3/";
                // The following are not supported features.
                requestItems.Items[0].EnableBuffering = false;
                requestItems.Items[0].EnableBufferingSpecified = false;
                requestItems.Items[0].ReqType = null;
                requestItems.Items[0].RequestedSamplingRate = 0;
                requestItems.Items[0].RequestedSamplingRateSpecified = false;

                requestItems.Items[1] = new SubscribeRequestItem();
                requestItems.Items[1].ClientItemHandle = clientRequestHandle;
                requestItems.Items[1].Deadband = 0.5f;
                requestItems.Items[1].DeadbandSpecified = true;
                requestItems.Items[1].ItemName = "MyVar42";
                requestItems.Items[1].ItemPath = "Root/Fold2/Fold3/";
                // The following are not supported features.
                requestItems.Items[1].EnableBuffering = false;
                requestItems.Items[1].EnableBufferingSpecified = false;
                requestItems.Items[1].ReqType = null;
                requestItems.Items[1].RequestedSamplingRate = 0;
                requestItems.Items[1].RequestedSamplingRateSpecified = false;

                // The following are not supported features.
                requestItems.Deadband = 0.0f;
                requestItems.DeadbandSpecified = false;
                requestItems.EnableBuffering = false;
                requestItems.EnableBufferingSpecified = false;
                requestItems.ItemPath = null;
                requestItems.ReqType = null;
            }

            SubscribeRequest SR = new SubscribeRequest(
                ro,
                requestItems,
                returnValues,
                pingRate);

            // Perform the call to service and get the response.
            SubscribeResponse res = svc.Subscribe(SR);

            PrintResponse(res);

            return res;
        }

        /// <summary>
        /// Performs SubscriptionPolledRefresh request, printing and returning response.
        /// </summary>
        /// <param name="localeID">Locale id for the request.</param>
        /// <param name="clientRequestHandle">Client request handle (identifier) for the request.</param>
        /// <param name="sids">Array containing Subscription identifiers to be refreshed.</param>
        /// <param name="holdTime">Hold time for advanced mode.</param>
        /// <param name="waitTime">Wait time for advanced mode.</param>
        /// <param name="returnAllItems">Specifies whether or not all Items must be returned along with the response, even if they did not change.</param>
        /// <param name="returnItemName">Specifies whether or not Item names must be returned along with the response.</param>
        /// <param name="returnItemPath">Specifies whether or not Item paths must be returned along with the response.</param>
        /// <param name="returnItemTime">Specifies whether or not Item timestamp must be returned along with the response.</param>
        /// <param name="returnDiagnosticInfo">Specifies whether or not Item diagnostics information must be returned along with the response.</param>
        /// <param name="returnErrorText">Specifies whether or not detailed error information must be returned along with the response, if any.</param>
        /// <param name="requestDeadlineSpecified"><b>Not supported feature.</b> See OPC XML DA 1.0 specification for more description about this field.</param>
        /// <returns>The response to the SubscriptionPolledRefresh request.</returns>
        public static SubscriptionPolledRefreshResponse SubscriptionPolledRefresh(
            string localeID,
            string clientRequestHandle,
            string[] sids,
            DateTime holdTime,
            int waitTime,
            bool returnAllItems,
            bool returnItemName = true,
            bool returnItemPath = true,
            bool returnItemTime = true,
            bool returnDiagnosticInfo = false,
            bool returnErrorText = false,
            // Not supported features down here
            bool requestDeadlineSpecified = false)
        {
            // Get the channel.
            IOCXSService svc = GetOCXSChannel(EndpointAddress);

            // Create request options, for ReadRequest.
            RequestOptions ro = new RequestOptions();
            ro.ClientRequestHandle = clientRequestHandle;
            ro.LocaleID = localeID;
            ro.RequestDeadline = DateTime.Parse("2001-09-11");
            ro.RequestDeadlineSpecified = requestDeadlineSpecified;
            ro.ReturnDiagnosticInfo = returnDiagnosticInfo;
            ro.ReturnErrorText = returnErrorText;
            ro.ReturnItemName = returnItemName;
            ro.ReturnItemPath = returnItemPath;
            ro.ReturnItemTime = returnItemTime;

            // Even though sids is a string[], please mind that
            // just the 1st element (sids[0]) will be considered
            // during the SubscriptionPolledRefresh call.
            SubscriptionPolledRefreshRequest SPRR = 
                new SubscriptionPolledRefreshRequest(
                    ro,
                    sids,
                    holdTime,
                    waitTime,
                    returnAllItems);

            // Perform the call to service and get the response.
            SubscriptionPolledRefreshResponse res = svc.SubscriptionPolledRefresh(SPRR);

            PrintResponse(res);

            return res;
        }

        /// <summary>
        /// Performs SubscriptionPolledRefresh request, printing and returning response.
        /// </summary>
        /// <param name="sid">Subscription identifier to be cancelled.</param>
        /// <param name="clientRequestHandle">Client request handle (identifier) for the request.</param>
        /// <returns>The response to the SubscriptionCancel request.</returns>
        public static SubscriptionCancelResponse SubscriptionCancel(string sid, string clientRequestHandle)
        {
            // Get the channel.
            IOCXSService svc = GetOCXSChannel(EndpointAddress);

            // This request needs just the Subscription identifier (`sid`) and a handle to
            // identify the request in the system.
            SubscriptionCancelRequest SCR = new SubscriptionCancelRequest(sid, clientRequestHandle);

            SubscriptionCancelResponse res = svc.SubscriptionCancel(SCR);

            PrintResponse(res);
            
            return res;
        }
        

        #endregion


        #region Output Helpers

        /// <summary>
        /// Prints OPC XML DA SubscriptionCancel provided response.
        /// </summary>
        /// <param name="res">The provided response.</param>
        public static void PrintResponse(SubscriptionCancelResponse res) {
            Console.WriteLine(
                "*** SubscriptionCancelResponse ***\n" +
                " ClientRequestHandle: " + ((res.ClientRequestHandle == null) ? "''" : res.ClientRequestHandle));
        }

        /// <summary>
        /// Prints OPC XML DA SubscriptionPolledRefresh provided response.
        /// </summary>
        /// <param name="res">The provided response.</param>
        public static void PrintResponse(SubscriptionPolledRefreshResponse res) {
            Console.WriteLine(
                "*** SubscriptionPolledRefreshResponse ***\n" +
                " Errors: " + GetString(res.Errors) + "\n" +
                " ### " + "\n" +
                " SubscribeResult: " + GetString(res.SubscriptionPolledRefreshResult) + "\n" +
                " ### " + "\n" +
                " RItemList: " + GetString(res.RItemList) + "\n" +
                " ### " + "\n" +
                " InvalidServerSubHandles: " + GetString(res.InvalidServerSubHandles) + "\n" +
                " ### " + "\n" +
                " DataBufferOverflow: " + res.DataBufferOverflow);
        }

        /// <summary>
        /// Prints OPC XML DA Subscribe provided response.
        /// </summary>
        /// <param name="res">The provided response.</param>
        public static void PrintResponse(SubscribeResponse res) {
            Console.WriteLine(
                "*** SubscribeResponse ***\n" +
                " Errors: " + GetString(res.Errors) + "\n" +
                " ### " + "\n" +
                " SubscribeResult: " + GetString(res.SubscribeResult) + "\n" +
                " ### " + "\n" +
                " ServerSubHandle: " + ((res.ServerSubHandle == null) ? "''" : res.ServerSubHandle) + "\n" +
                " ### " + "\n" +
                " RItemList: " + GetString(res.RItemList));
        }

        /// <summary>
        /// Prints OPC XML DA Write provided response.
        /// </summary>
        /// <param name="res">The provided response.</param>
        public static void PrintResponse(WriteResponse res) {
            Console.WriteLine(
                "*** WriteResponse ***\n" +
                " Errors: " + GetString(res.Errors) + "\n" +
                " ### " + "\n" +
                " WriteResult: " + GetString(res.WriteResult) + "\n" +
                " ### " + "\n" +
                " RItemList: " + GetString(res.RItemList));
        }

        /// <summary>
        /// Prints OPC XML DA Read provided response.
        /// </summary>
        /// <param name="res">The provided response.</param>
        public static void PrintResponse(ReadResponse res) {
            Console.WriteLine(
                "*** ReadResponse ***\n" +
                " Errors: " + GetString(res.Errors) + "\n" +
                " ### " + "\n" +
                " ReadResult: " + GetString(res.ReadResult) +
                " ### " + "\n" +
                " RItemList: " + GetString(res.RItemList));
        }

        /// <summary>
        /// Prints OPC XML DA GetProperties provided response.
        /// </summary>
        /// <param name="res">The provided response.</param>
        public static void PrintResponse(GetPropertiesResponse res) {
            Console.WriteLine(
                "*** GetPropertiesResponse ***" + "\n" +
                " Errors: " + GetString(res.Errors) + "\n" +
                " ### " + "\n" +
                " GetPropertiesResult: " + GetString(res.GetPropertiesResult) +
                " ### " + "\n" +
                " PropertyList: " + GetString(res.PropertyLists));
        }
        
        /// <summary>
        /// Prints OPC XML DA GetStatus provided response.
        /// </summary>
        /// <param name="res">The provided response.</param>
        public static void PrintResponse(GetStatusResponse res) {
            Console.WriteLine(
                "*** GetStatusResponse ***" + "\n" +
                " GetStatusResult: " + GetString(res.GetStatusResult) +
                " ### " + "\n" +
                " Status: " + GetString(res.Status));
        }

        /// <summary>
        /// Prints OPC XML DA Browse provided response.
        /// </summary>
        /// <param name="res">The provided response.</param>
        public static void PrintResponse(BrowseResponse res) {
            Console.WriteLine(
                "*** BrowseResponse ***" + "\n" +
                " BrowseResult: " + GetString(res.BrowseResult) +
                " ### " + "\n" +
                " ContinuationPoint: " + res.ContinuationPoint + "\n" +
                " ### " + "\n" +
                " Elements: " + GetString(res.Elements) + "\n" +
                " ### " + "\n" +
                " Errors: " + GetString(res.Errors) + "\n" +
                " ### " + "\n" +
                " MoreElements: " + res.MoreElements);
        }

        /// <summary>
        /// Gets <c>string</c> representation of an array of <c>string</c>s.
        /// </summary>
        /// <param name="array">The array of strings to be represented.</param>
        /// <returns>The <c>string</c> representation of an array of <c>string</c>s.</returns>
        public static string GetString(string[] array) {
            if (array == null)
                return "[ ]";
            
            string res = "[ ";

            foreach (string el in array)
                res += el + ", ";

            res += " ]";

            return res;
        }

        /// <summary>
        /// Gets <c>string</c> representation of an array of <c>interfaceVersion</c>.
        /// </summary>
        /// <param name="array">The array of <c>interfaceVersion</c> to be represented.</param>
        /// <returns>The <c>string</c> representation of an array of <c>interfaceVersion</c>.</returns>
        public static string GetString(interfaceVersion[] array) {
            if (array == null)
                return "[ ]";            
            
            string res = "[ ";

            foreach (interfaceVersion el in array)
                res += el.ToString() + ", ";

            res += " ]";

            return res;
        }

        /// <summary>
        /// Gets <c>string</c> representation of an array of <c>BrowseElement</c>.
        /// </summary>
        /// <param name="array">The array of <c>BrowseElement</c> to be represented.</param>
        /// <returns>The <c>string</c> representation of an array of <c>interfaceVersion</c>.</returns>
        public static string GetString(BrowseElement[] array) {
            if (array == null)
                return "[ ]";
            
            string res = "[ ";

            foreach (BrowseElement el in array)
                res += GetString(el);

            res += " ]";

            return res;
        }

        /// <summary>
        /// Gets <c>string</c> representation of an array of <c>ItemProperty</c>.
        /// </summary>
        /// <param name="array">The array of <c>ItemProperty</c> to be represented.</param>
        /// <returns>The <c>string</c> representation of an array of <c>interfaceVersion</c>.</returns>
        public static string GetString(ItemProperty[] array) {
            if (array == null)
                return "[ ]";
            
            string res = "[ ";

            foreach (ItemProperty el in array)
                res += GetString(el);

            res += " ]";

            return res;
        }

        /// <summary>
        /// Gets <c>string</c> representation of an array of <c>OPCError</c>.
        /// </summary>
        /// <param name="array">The array of <c>OPCError</c> to be represented.</param>
        /// <returns>The <c>string</c> representation of an array of <c>interfaceVersion</c>.</returns>
        public static string GetString(OPCError[] array) {
            if (array == null)
                return "[ ]";
            
            string res = "[ ";

            foreach (OPCError el in array)
                res += GetString(el);

            res += " ]";

            return res;            
        }

        /// <summary>
        /// Gets <c>string</c> representation of an array of <c>ItemValue</c>.
        /// </summary>
        /// <param name="array">The array of <c>ItemValue</c> to be represented.</param>
        /// <returns>The <c>string</c> representation of an array of <c>interfaceVersion</c>.</returns>
        public static string GetString(ItemValue[] array) {
            if (array == null)
                return "[ ]";

            string res = "[ ";

            foreach (ItemValue el in array)
                res += GetString(el);

            res += " ]";

            return res; 
        }

        /// <summary>
        /// Gets <c>string</c> representation of an array of <c>PropertyReplyList</c>.
        /// </summary>
        /// <param name="array">The array of <c>PropertyReplyList</c> to be represented.</param>
        /// <returns>The <c>string</c> representation of an array of <c>interfaceVersion</c>.</returns>
        public static string GetString(PropertyReplyList[] array) {
            if (array == null)
                return "[ ]";

            string res = "[ ";

            foreach (PropertyReplyList el in array)
                res += GetString(el);

            res += " ]";

            return res; 
        }

        /// <summary>
        /// Gets <c>string</c> representation of an array of <c>SubscribeItemValue</c>.
        /// </summary>
        /// <param name="array">The array of <c>SubscribeItemValue</c> to be represented.</param>
        /// <returns>The <c>string</c> representation of an array of <c>interfaceVersion</c>.</returns>
        public static string GetString(SubscribeItemValue[] array)
        {
            if (array == null)
                return "[ ]";

            string res = "[ ";

            foreach (SubscribeItemValue el in array)
                res += GetString(el);

            res += " ]";

            return res;
        }

        /// <summary>
        /// Gets <c>string</c> representation of an array of <c>SubscribePolledRefreshReplyItemList</c>.
        /// </summary>
        /// <param name="array">The array of <c>SubscribePolledRefreshReplyItemList</c> to be represented.</param>
        /// <returns>The <c>string</c> representation of an array of <c>interfaceVersion</c>.</returns>
        public static string GetString(SubscribePolledRefreshReplyItemList[] array) {
            if (array == null)
                return "[ ]";

            string res = "[ ";

            foreach (SubscribePolledRefreshReplyItemList el in array)
                res += GetString(el);

            res += " ]";

            return res;
        }

        /// <summary>
        /// Gets <c>string</c> representation of a <c>SubscribePolledRefreshReplyItemList</c>.
        /// </summary>
        /// <param name="el">The provided <c>SubscribePolledRefreshReplyItemList</c>.</param>
        /// <returns>A <c>string</c> representation of a <c>SubscribePolledRefreshReplyItemList</c>.</returns>
        public static string GetString(SubscribePolledRefreshReplyItemList el) {
            return "\n*** SubscribePolledRefreshReplyItemList ***\n" +
                   "\tItems: " + GetString(el.Items) + "\n" +
                   "\tSubscriptionHandle: " + ((el.SubscriptionHandle == null) ? "''" : el.SubscriptionHandle);
        }

        /// <summary>
        /// Gets <c>string</c> representation of a <c>SubscribeItemValue</c>.
        /// </summary>
        /// <param name="el">The provided <c>SubscribeItemValue</c>.</param>
        /// <returns>A <c>string</c> representation of a <c>SubscribeItemValue</c>.</returns>
        public static string GetString(SubscribeItemValue el) {
            return "\n*** SubscribeItemValue ***\n" +
                   "\tItem: " + GetString(el.ItemValue) + "\n" +
                   "\tRevisedSamplingRate: " + el.RevisedSamplingRate.ToString() + "\n" +
                   "\tRevisedSamplingRate: " + el.RevisedSamplingRateSpecified + "\n";
        }

        /// <summary>
        /// Gets <c>string</c> representation of a <c>SubscribeReplyItemList</c>.
        /// </summary>
        /// <param name="el">The provided <c>SubscribeReplyItemList</c>.</param>
        /// <returns>A <c>string</c> representation of a <c>SubscribeReplyItemList</c>.</returns>
        public static string GetString(SubscribeReplyItemList el) {
            return (el == null)
                       ? "''"
                       : "\n*** SubscribeReplyItemList ***\n" +
                         "\t\tRevisedSamplingRate: " + el.RevisedSamplingRate.ToString() + "\n" +
                         "\t\tRevisedSamplingRateSpecified: " + el.RevisedSamplingRateSpecified + "\n" +
                         "\t\tItem: " + GetString(el.Items);
        }

        /// <summary>
        /// Gets <c>string</c> representation of a <c>ItemValue</c>.
        /// </summary>
        /// <param name="el">The provided <c>ItemValue</c>.</param>
        /// <returns>A <c>string</c> representation of a <c>ItemValue</c>.</returns>
        public static string GetString(ItemValue el) {
            return "\n*** ItemValue ***\n" +
                   "\t\tClientItemHandle: " + ((el.ClientItemHandle == null) ? "''" : el.ClientItemHandle) + "\n" +
                   "\t\tDiagnosticInfo: " + ((el.DiagnosticInfo == null) ? "''" : el.DiagnosticInfo) + "\n" +
                   "\t\tItemName: " + ((el.ItemName == null) ? "''" : el.ItemName) + "\n" +
                   "\t\tItemPath: " + ((el.ItemPath == null) ? "''" : el.ItemPath) + "\n" +
                   "\t\tQuality: " + GetString(el.Quality) + "\n" +
                   "\t\tResultID: " + ((el.ResultID == null) ? "''" : el.ResultID.ToString()) + "\n" +
                   "\t\tTimestamp: " + el.Timestamp + "\n" +
                   "\t\tTimestampSpecified: " + el.TimestampSpecified + "\n" +
                   "\t\tValue: " + ((el.Value == null) ? "''" : el.Value) + "\n" +
                   "\t\tValueTypeQualifier: " + ((el.ValueTypeQualifier == null) ? "''" : el.ValueTypeQualifier.ToString());
        }

        /// <summary>
        /// Gets <c>string</c> representation of a <c>OPCQuality</c>.
        /// </summary>
        /// <param name="el">The provided <c>OPCQuality</c>.</param>
        /// <returns>A <c>string</c> representation of a <c>OPCQuality</c>.</returns>
        public static string GetString(OPCQuality el) {
            return "\n*** OPCQuality ***\n" +
                   "\t\t\tQualityField: " + el.QualityField + "\n" +
                   "\t\t\tLimitField: " + el.LimitField + "\n" +
                   "\t\t\tVendorField: " +  el.VendorField;
        }

        /// <summary>
        /// Gets <c>string</c> representation of a <c>ReplyItemList</c>.
        /// </summary>
        /// <param name="el">The provided <c>ReplyItemList</c>.</param>
        /// <returns>A <c>string</c> representation of a <c>ReplyItemList</c>.</returns>
        public static string GetString(ReplyItemList el) {
            return "\n*** ReplyItemList ***" + "\n" +
                   "\tItemList: " + GetString(el.Items);
        }


        /// <summary>
        /// Gets <c>string</c> representation of a <c>PropertyReplyList</c>.
        /// </summary>
        /// <param name="el">The provided <c>PropertyReplyList</c>.</param>
        /// <returns>A <c>string</c> representation of a <c>PropertyReplyList</c>.</returns>
        public static string GetString(PropertyReplyList el) {
            return "\n*** PropertyReplyList ***" + "\n" +
                   "\tItemName: " + ((el.ItemName == null) ? "''" : el.ItemName) + "\n" +
                   "\tItemPath: " + ((el.ItemPath == null) ? "''" : el.ItemPath) +"\n" +
                   "\tProperties: " + GetString(el.Properties) + "\n" +
                   "\tResultID: " + ((el.ResultID == null) ? "''" : el.ResultID.ToString());
        }

        /// <summary>
        /// Gets <c>string</c> representation of a <c>OPCError</c>.
        /// </summary>
        /// <param name="el">The provided <c>OPCError</c>.</param>
        /// <returns>A <c>string</c> representation of a <c>OPCError</c>.</returns>
        public static string GetString(OPCError el) {
            return "\n*** OPCError ***" + "\n" +
                   "\tID: " + ((el.ID == null) ? "''" : el.ID.ToString()) +"\n" +
                   "\tText: " + ((el.Text == null) ? "''" : el.Text) + "\n";
        }

        /// <summary>
        /// Gets <c>string</c> representation of a <c>ItemProperty</c>.
        /// </summary>
        /// <param name="el">The provided <c>ItemProperty</c>.</param>
        /// <returns>A <c>string</c> representation of a <c>ItemProperty</c>.</returns>
        public static string GetString(ItemProperty el) {
            return "\n*** ItemProperty ***" + "\n" +
                   "\t\tDescription: " + ((el.Description == null) ? "''" : el.Description) + "\n" +
                   "\t\tItemName: " + ((el.ItemName == null) ? "''" : el.ItemName) + "\n" +
                   "\t\tItemPath: " + ((el.ItemPath == null) ? "''" : el.ItemPath) + "\n" +
                   "\t\tName: " + ((el.Name == null) ? "''" : el.Name.ToString()) + "\n" +
                   "\t\tResultID: " + ((el.ResultID == null) ? "''" : el.ResultID.ToString()) + "\n" +
                   "\t\tValue: " + ((el.Value == null) ? "''" : el.Value) + "\n";
        }

        /// <summary>
        /// Gets <c>string</c> representation of a <c>BrowseElement</c>.
        /// </summary>
        /// <param name="el">The provided <c>BrowseElement</c>.</param>
        /// <returns>A <c>string</c> representation of a <c>BrowseElement</c>.</returns>
        public static string GetString(BrowseElement el) {
            return "\n*** BrowseElement ***" + "\n" +
                   "\tHasChildren: " + el.HasChildren + "\n" +
                   "\tIsItem: " + el.IsItem + "\n" +
                   "\tItemName: " + ((el.ItemName == null) ? "''" : el.ItemName) + "\n" +
                   "\tItemPath: " + ((el.ItemPath == null) ? "''" : el.ItemPath) + "\n" +
                   "\tName: " + ((el.Name == null) ? "''" : el.Name) + "\n" +
                   "\tProperties: " + GetString(el.Properties) + "\n";
        }
        
        /// <summary>
        /// Gets <c>string</c> representation of a <c>ServerStatus</c>.
        /// </summary>
        /// <param name="el">The provided <c>ServerStatus</c>.</param>
        /// <returns>A <c>string</c> representation of a <c>ServerStatus</c></returns>
        public static string GetString(ServerStatus el) {
            return "*** ServerStatus ***" + "\n" +
                   "ProductVersion: " + ((el.ProductVersion == null) ? "''" : el.ProductVersion) + "\n" +
                   "StartTime: " + ((el.StartTime == null) ? "''" : el.StartTime.ToString()) + "\n" +
                   "StatusInfo: " + ((el.StatusInfo == null) ? "''" : el.StatusInfo) + "\n" +
                   "SupportedInterfaceVersions: " + GetString(el.SupportedInterfaceVersions) + "\n" +
                   "SupportedLocaleIDs: " + GetString(el.SupportedLocaleIDs) + "\n" +
                   "VendorInfo: " + ((el.VendorInfo == null) ? "''" : el.VendorInfo) + "\n";
        }

        /// <summary>
        /// Gets <c>string</c> representation of a <c>ReplyBase</c>.
        /// </summary>
        /// <param name="el">The provided <c>ReplyBase</c>.</param>
        /// <returns>A <c>string</c> representation of a <c>ReplyBase</c>.</returns>
        public static string GetString(ReplyBase el) {
            return "*** ReplyBase ***" + "\n" +
                   "\tClientRequestHandle: " + ((el.ClientRequestHandle == null) ? "''" : el.ClientRequestHandle) + "\n" +
                   "\tRcvTime: " + ((el.RcvTime == null) ? "''" : el.RcvTime.ToString()) + "\n" +
                   "\tReplyTime: " + ((el.ReplyTime == null) ? "''" : el.ReplyTime.ToString()) + "\n" +
                   "\tRevisedLocaleID: " + ((el.RevisedLocaleID == null) ? "''" : el.RevisedLocaleID) + "\n" +
                   "\tServerState: " + el.ServerState + "\n";
        }

        #endregion
        
    }

}
